﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
// ReSharper disable RedundantSuppressNullableWarningExpression

namespace DimensionsHelper.Common.Utils;

public class RegexManager
{
    private readonly List<string> _buffer = [];
    private readonly List<RegexElement> _elements = [];
    private readonly HashSet<string> _names = [];
    private readonly List<string> _originalItems = [];
    private bool _allowEndWithSpace;
    private bool _allowStartWithSpace;
    private bool _exactly;
    private string _expression = string.Empty;

    private EventHandler<RegexElementFormatArgs>? _formatElementHandler;

    private bool _ignoreCase;
    private bool _isDirty = true;
    private Regex? _regExpr;
    private string _splitter = "";


    public string GroupNameFromNumber(int number)
    {
        return _regExpr!.GroupNameFromNumber(number);
    }


    /// <summary>
    ///     构建的正则表达式是否忽略大小写
    /// </summary>
    public bool IgnoreCase
    {
        get => _ignoreCase;

        set => SetProperty(ref _ignoreCase, value);
    }

    /// <summary>
    ///     是否全字匹配，如果此值为true，表达式元素将以^开头、以$结尾。
    /// </summary>
    public bool Exactly
    {
        get => _exactly;

        set => SetProperty(ref _exactly, value);
    }

    /// <summary>
    ///     是否允许各个元素开头有空格。
    /// </summary>
    public bool AllowStartWithSpace
    {
        get => _allowStartWithSpace;

        set => SetProperty(ref _allowStartWithSpace, value);
    }

    /// <summary>
    ///     是否允许各个元素末尾有空格。
    /// </summary>
    public bool AllowEndWithSpace
    {
        get => _allowEndWithSpace;

        set => SetProperty(ref _allowEndWithSpace, value);
    }


    /// <summary>
    ///     表达式查询内容的分隔符，此分隔符不会进入捕获组。
    /// </summary>
    public string Splitter
    {
        get => _splitter;

        set
        {
            _splitter = value;
            _isDirty = true;
        }
    }

    /// <summary>
    ///     当前已添加的元素数量。
    /// </summary>
    public int Count => _buffer.Count;

    private void SetProperty<T>(ref T field, T val)
    {
        if ((field == null && val == null) || (field != null && field.Equals(val)))
        {
            return;
        }

        field = val;
        _isDirty = true;
    }

    /// <summary>
    ///     添加新的文本元素时，会先对传入文本执行此回调，再将返回结果添加进表达式末尾。
    /// </summary>
    public event EventHandler<RegexElementFormatArgs> FormatElement
    {
        add => _formatElementHandler += value;
        remove => _formatElementHandler -= value;
    }

    /// <summary>
    ///     获取指定索引的原始元素文本。
    /// </summary>
    /// <param name="index">给定索引</param>
    /// <returns>指定索引的原始文本</returns>
    public string GetOriginalAt(int index)
    {
        return _originalItems[index];
    }

    /// <summary>
    ///     获取指定索引的缓冲区元素。
    /// </summary>
    /// <param name="index"></param>
    /// <returns>格式化后的正则表达式文本</returns>
    public string GetBufferAt(int index)
    {
        return _buffer[index];
    }

    /// <summary>
    ///     查找指定文本在原始文本中的索引，不区分大小写。
    /// </summary>
    /// <param name="text">待查找的文本</param>
    /// <returns>如果成功找到，返回索引，否则，返回-1</returns>
    public int FindOriginalIndex(string text)
    {
        return _originalItems.FindIndex(e => e.Equals(text, StringComparison.OrdinalIgnoreCase));
    }

    private string GetFormattedCaptureText(string text, string? name)
    {
        if (AllowStartWithSpace)
        {
            text = "\\s*" + text;
        }

        if (AllowEndWithSpace)
        {
            text += "\\s*";
        }

        if (Exactly)
        {
            if (string.IsNullOrEmpty(Splitter))
            {
                text = "^" + text + "$";
            }
            else
            {
                text = $"(?:^|(?<={Splitter}))" + text + $"(?:$|(?={Splitter}))";
            }
        }
        else if (!string.IsNullOrEmpty(Splitter))
        {
            text += $"(?:{Splitter}?)";
        }

        if (!string.IsNullOrEmpty(name))
        {
            text = $"?<{name}>" + text;
        }

        return "(" + text + ")";
    }

    private string GetFormattedText(RegexElement element)
    {
        string text = "";

        if (!string.IsNullOrEmpty(element.Expression))
        {
            text = element.Expression;

            if (!string.IsNullOrEmpty(element.Name))
            {
                return $"(?<{element.Name}>{text})";
            }

            return "(" + text + ")";
        }

        if (_formatElementHandler != null)
        {
            RegexElementFormatArgs args = element.Originals.Count == 1
                ? new RegexElementFormatArgs(element.Originals[0])
                : new RegexElementFormatArgs(element.Originals);
            _formatElementHandler(this, args);
            text = args.Text;

            if (args.Handled)
            {
                return text;
            }
        }

        if (string.IsNullOrEmpty(text))
        {
            return GetFormattedCaptureText(
                string.Join("|", element.Originals.Select(StringHelper.EscapeRegExpr)),
                element.Name);
        }

        return GetFormattedCaptureText(StringHelper.EscapeRegExpr(text), element.Name);
    }

    private string GetFormattedText(string text, string? name)
    {
        if (_formatElementHandler != null)
        {
            var args = new RegexElementFormatArgs(text);
            _formatElementHandler(this, args);
            text = args.Text;

            if (args.Handled)
            {
                return text;
            }
        }

        return GetFormattedCaptureText(StringHelper.EscapeRegExpr(text), name);
    }

    private void CheckRegexCaptureGroupName(string? name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return;
        }

        if (!_names.Add(name!))
        {
            throw new Exception($"正则表达式捕获组命名'{name}'已存在。");
        }
    }

    /// <summary>
    ///     向当前表达式结尾添加新的文本元素。
    /// </summary>
    /// <param name="text">追加的文本内容</param>
    /// <param name="name">正则表达式捕获组名称，不配置名称的话，设置为null</param>
    public void Append(string text, string? name = null)
    {
        CheckRegexCaptureGroupName(name);
        _buffer.Add(GetFormattedText(text, name));
        _originalItems.Add(text);
        _elements.Add(new RegexElement(name: name, originals: new List<string> { text }));
        _isDirty = true;
    }

    /// <summary>
    ///     向当前表达式中的末尾添加新的文本元素，已文本列表内容为捕获组。
    /// </summary>
    /// <param name="texts">输入文本列表</param>
    /// <param name="name">正则表达式捕获组名称，不配置的话，设置为null</param>
    public void Append(IList<string> texts, string? name = null)
    {
        CheckRegexCaptureGroupName(name);
        var element = new RegexElement(name: name, originals: new List<string>(texts));
        _buffer.Add(GetFormattedText(element));
        _originalItems.AddRange(texts);
        _elements.Add(element);
        _isDirty = true;
    }

    /// <summary>
    ///     将给定文本追加到给定索引的元素末尾。
    /// </summary>
    /// <param name="text">追加的文本</param>
    /// <param name="index">指定的索引</param>
    public void Append(string text, int index)
    {
        _originalItems.Add(text);
        _elements[index].Originals.Add(text);
        _buffer[index] = GetFormattedText(_elements[index]);
        _isDirty = true;
    }

    /// <summary>
    ///     将给定正则表达式追加到当前表达式末尾。
    /// </summary>
    /// <param name="expression">指定正则表达式</param>
    /// <param name="name">匹配分组名，可以忽略</param>
    public void AppendExpression(string expression, string? name = null)
    {
        CheckRegexCaptureGroupName(name);
        _originalItems.Add(expression);
        _buffer.Add(expression);
        _elements.Add(new RegexElement(expression, name));
        _isDirty = true;
    }

    /// <summary>
    ///     将给定正则表达式列表追加到当前表达式末尾。
    /// </summary>
    /// <param name="expressions">待添加的表达式集合</param>
    /// <param name="name">表达式分组名</param>
    public void AppendExpression(IList<string> expressions, string? name = null)
    {
        CheckRegexCaptureGroupName(name);
        var element = new RegexElement(name: name, expression: string.Join("|", expressions));
        _originalItems.AddRange(expressions);
        _buffer.Add(element.Expression);
        _elements.Add(element);
        _isDirty = true;
    }

    /// <summary>
    ///     使用当前表达式匹配输入文本。
    /// </summary>
    /// <param name="input">指定输入文本</param>
    /// <returns>第一个匹配项</returns>
    public Match Match(string input)
    {
        Refresh();
        return _regExpr!.Match(input);
    }

    /// <summary>
    ///     使用当前表达式匹配输入文本。
    /// </summary>
    /// <param name="input">指定输入文本</param>
    /// <returns>所有匹配项</returns>
    public MatchCollection Matches(string input)
    {
        Refresh();
        return _regExpr!.Matches(input);
    }

    /// <summary>
    ///     判断给定字符串是否匹配成功。
    /// </summary>
    /// <param name="input">指定输入字符串</param>
    public bool IsMatch(string input)
    {
        Refresh();
        return _regExpr!.IsMatch(input);
    }


    public class RegexGroupInfo(Match matchObject, int index, string groupName, Group groupObject)
    {
        public Match MatchObject { get; } = matchObject;
        public int Index { get; } = index;
        public string GroupName { get; } = groupName;
        public Group GroupObject { get; } = groupObject;

        public void Deconstruct(out Match matchObject, out int index, out string groupName, out Group groupObject)
        {
            matchObject = MatchObject;
            index = Index;
            groupName = GroupName;
            groupObject = GroupObject;
        }
    }


    /// <summary>
    ///     使用当前表达式匹配输入文本，获取所有匹配到的表达式捕获组。
    /// </summary>
    /// <param name="input">待匹配文本</param>
    /// <returns>成功匹配到的捕获组列表</returns>
    public List<RegexGroupInfo> SearchGroups(string input)
    {
        input = input.Trim();

        // 允许多匹配
        MatchCollection matches = Matches(input);
        var groups = new List<RegexGroupInfo>();

        for (int i = 0; i < matches.Count; i++)
        {
            Match match = matches[i];

            if (!match.Success)
            {
                continue;
            }

            for (int j = 1; j < match.Groups.Count; j++)
            {
                Group group = match.Groups[j];
                if (group.Success)
                {
                    groups.Add(new RegexGroupInfo(match, i, GroupNameFromNumber(j), group));
                }
            }
        }

        return groups;
    }

    /// <summary>
    ///     使用当前表达式匹配输入文本，获取第一个匹配成功的表达式捕获组索引，如果匹配失败或不包含捕获组，返回-1。
    /// </summary>
    /// <param name="input">待匹配文本</param>
    /// <returns>匹配成功并且包含捕获组，返回捕获组索引，否则，返回-1</returns>
    public int SearchIndex(string input)
    {
        input = input.Trim();

        Match match = Match(input);

        if (!match.Success)
        {
            return -1;
        }

        for (int i = 1; i < match.Groups.Count; i++)
        {
            if (match.Groups[i].Success)
            {
                return i - 1;
            }
        }

        return -1;
    }

    /// <summary>
    ///     使用当前表达式匹配输入文本，并获取对应索引的原始文本。
    /// </summary>
    /// <param name="input">待匹配文本</param>
    /// <returns>如果输入文本匹配成功，返回添加时的原始文本，否则，返回null</returns>
    public string? SearchOriginal(string input)
    {
        int index = SearchIndex(input);
        return index == -1 ? null : _originalItems[index];
    }


    /// <summary>
    ///     依据当前缓冲列表已有内容更新正则表达式
    /// </summary>
    private void Refresh()
    {
        if (!_isDirty)
        {
            _regExpr ??= new Regex(_expression, IgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
            return;
        }

        _buffer.Clear();

        foreach (RegexElement element in _elements)
        {
            _buffer.Add(GetFormattedText(element));
        }

        _expression = string.Join("|", _buffer);
        _regExpr = new Regex(_expression, IgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
        _isDirty = false;
    }

    /// <summary>
    ///     清空已有文本
    /// </summary>
    public void Clear()
    {
        _buffer.Clear();
        _expression = string.Empty;
        _elements.Clear();
        _originalItems.Clear();
        _names.Clear();

        _regExpr = null;
        _isDirty = false;
    }

    private class RegexElement(string expression = "", string? name = null, IList<string>? originals = null)
    {
        public string Expression { get; } = expression;

        public string? Name { get; } = name;

        public IList<string> Originals { get; } = originals ?? [];
    }
}